/**
 * Copyright 2002 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys .NET Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
//@Author	: Lejil 
//@Class	: ManagedCallHandler

#pragma once
#include "stdafx.h"
#include "ManagedCallHandler.h"
#include "MetaData.h"
#include "CTSToNative.h"
//#include "Tupable.h"


//   First create the instance of the class with default constructor
//       and then invoke the method
bool ManagedCallHandler::InvokeInstanceMethod(int response)
{
	Object   * result			=	NULL;
	Object	 * classInstance	=	NULL;
	try
	{
		classInstance			=   Activator::CreateInstance(classType);
	   	BindingFlags flags		=   static_cast<BindingFlags>(BindingFlags::Public,BindingFlags::Instance,BindingFlags::OptionalParamBinding,BindingFlags::InvokeMethod);
		result					=   classType->InvokeMember(parameters->Method,flags,NULL,classInstance,parameters->Mtd_Arguments);
		return CreateResponse(result,response);
	}
	catch(Exception *ex)
	{
		throw ex;
	}
	__finally
	{
		classInstance	=	NULL;
	}
}



// Invoke the .NET public static method, from the classType
//           create the response back from the result
//
bool ManagedCallHandler::InvokeStaticMethod(int response)
{
	try
	{
		Object   * result		=	NULL;
		BindingFlags flags		=	static_cast<BindingFlags>(BindingFlags::Public,BindingFlags::Static,BindingFlags::OptionalParamBinding,BindingFlags::InvokeMethod);
		result					=	classType->InvokeMember(parameters->Method,flags,NULL,NULL,parameters->Mtd_Arguments);
		return 	CreateResponse(result,response);
	}
	catch(Exception *ex)
	{
		throw ex;
	}
}


//	Calls the MetaData Handler .
//    Loads the Assembly,and Get all the classes, methods based on the request
//
bool  ManagedCallHandler::InvokeMetaData(int response)
{
	MetaData *metaData =	NULL;
	try
	{
		Object   * result	=	NULL;
		metaData			=	new MetaData(parameters->Request);
		classType			=	metaData->GetType();
		BindingFlags flags	=	static_cast<BindingFlags>(BindingFlags::Public ,BindingFlags::Instance,BindingFlags::OptionalParamBinding,BindingFlags::InvokeMethod);
		result				=	classType->InvokeMember(parameters->Method,flags,NULL,metaData,parameters->Mtd_Arguments);
		return CreateResponse(result,response);
	}
	catch(Exception *ex)
	{
		throw ex;
	}
	__finally
	{
	 	delete(metaData);
		metaData  =	NULL;
	}
}


/**
* Invoke the  method to be called, after setting all the 
*		configuration
*/
bool  ManagedCallHandler::Invoke(int response)
{
	bool	  isInvoked = false;
	
	//static   	= 0x01;
	//instance 	= 0x02;
	//metadata 	= 0x03;
	try
	{
		switch(parameters->OpType)
		{
			case 0x01:
			{
				LoadClass();
				isInvoked	=	InvokeStaticMethod(response);
				break;
			}
			case 0x02:
			{
				LoadClass();
				isInvoked	=	InvokeInstanceMethod(response);
				break;
			}
			case 0x03:
			{
				isInvoked	=	InvokeMetaData(response);
				break;
			}
		}
	 }
	 catch(TargetException *tgEx)
	 {
		CTSToNative::CreateSoapFault(response,"Target.NotFound",tgEx->Message,tgEx->ToString());
	 }
	 catch(TargetParameterCountException *tgPrCntEx)
	 {
		CTSToNative::CreateSoapFault(response,"Arguments.Mismatch",tgPrCntEx->Message,tgPrCntEx->ToString());
	 }
	 catch(MissingMethodException *mmEx)
	 {
		CTSToNative::CreateSoapFault(response,"Method.Not-Found",mmEx->Message,mmEx->ToString());
	 }
	 catch(TargetInvocationException *tgInvEx)
	 {
		Exception *exInstance =tgInvEx->InnerException;
		CTSToNative::CreateSoapFault(response,"Method.Invocation Error",exInstance->Message,exInstance->ToString());
	 }
	 catch(FileNotFoundException *fNotEx)
	 {
		String * assemblyName= (fNotEx->Message)->Substring(0,(fNotEx->Message)->IndexOf(",")) ;
		String * errorMessage = String::Concat(assemblyName,
			new String("	--	Is Not Registered in Global Assembly Cache"));
		CTSToNative::CreateSoapFault(response,"Assembly.NotFound",errorMessage,fNotEx->ToString());		
	 }
	 catch(Exception  *ex)
	 {
		CTSToNative::CreateSoapFault(response,"Server.NotImplemented",ex->Message,ex->ToString());
	 }
	 __finally
	 {
		classType	=	NULL;
	 }
	 return isInvoked;
 }


//	Create the Response back , after  Invoking .NET Method
//  //Convert the result to Appropriate Type  and then create				
//       Response back
bool ManagedCallHandler::CreateResponse(Object *result,int response)
{
	Document __gc *document		=	Node::GetDocument(parameters->Request);
	TypeCode typeCode ;
	try
	{
		int methodResultHeader =	NULL;
		int	methodResult	   =	NULL;
		
		// To Do
		// Need to change this implementation for Tupable
		
		if ( classType->GetInterface("Tupable") != NULL )
		{
			methodResult		=	response;
		}
		else
		{
			int tuple			=	document->CreateElementWithParentNS("tuple",response);
			int old				=	document->CreateElementWithParentNS("old", tuple);
			methodResultHeader	=	document->CreateElementWithParentNS(parameters->Method, old);
			//	This is required , if the method argument itself is NOM Pointers
			//		Get the argument  Node,GET the data and append back to the response	
			//
			int argumentsData		=	parameters->ParameterDataNode;
			int nodeCount			=	Node::GetNumNextSiblings(argumentsData);
			int	argumentsType		=	Node::GetFirstChildElement(parameters->ParameterTypeNode);
		
			for(int i=0; i<= nodeCount ; i++)
			{
				//If the method arguments are XMl Node pointers , read the 
				// modified value and return
				if (Node::GetAttribute(argumentsType,"ct","")->Equals("elements"))
				{
					if (!Node::GetAttribute(argumentsType,"scope","in")->Equals("in"))
					{
						int nomData	=	Int32::Parse(Node::GetData(argumentsData,"0"));
						Node::AppendToChild(nomData,0,methodResultHeader);
					}
				}

				//If the method arguments are ByRef Type read modified value & Return
				else if (Node::GetAttribute(argumentsType,"ct","")->Equals("ByRef"))
				{
					String *argsData =parameters->Mtd_Arguments[i]->ToString();
					document->CreateTextElementWithParentNS(Node::GetName(argumentsType),argsData,methodResultHeader);
				}

				argumentsType =	Node::GetNextElement(argumentsType);
				argumentsData =	Node::GetNextElement(argumentsData); 	
			}
			methodResult	=	document->CreateElementWithParentNS(parameters->Method,methodResultHeader);
		}
		
		if (result == NULL)
				return true;
		
		typeCode			=		Type::GetTypeCode((result)->GetType());
		switch(typeCode)
		{
			case TypeCode::Int32:
			{
				if (parameters->IsNomPointer)
				{
					int resultNode	  = Int32::Parse(result->ToString());
					Node::AppendToChild(resultNode,0,methodResult);
				}
				else
				{
					document->CreateText(result->ToString(), methodResult);
				}
				break;
			}
			case TypeCode::Object:
			{
				if ((result->GetType()->FullName)->Equals("System.Xml.XmlDocument"))
				{
					XmlDocument *xmlDocument =static_cast<XmlDocument*>(result);
					int resultNode = document ->ParseString(xmlDocument->InnerXml);
					Node::AppendToChild(resultNode,0,methodResult);
					xmlDocument = NULL;
				}
				else
				{	
					CTSToNative::CreateSoapFault(response,"Trying to Return Object","Object Return Type is not Supported","");
				}
				break;
			}
			default :
			{
				  document->CreateText(result->ToString(), methodResult);
				  break;
			}

		}
	}
	catch(Exception *ex)
	{
		throw ex;
	}
	__finally
	{
		delete document;
		document =NULL;
		result= NULL;
	}
	return true;
}

//
// Load the Assembly
//
void ManagedCallHandler::LoadClass()
{
	Assembly  * assembly = NULL;
	try
	{
		assembly	=	Assembly::Load(parameters->Assembly);
		classType	=	assembly->GetType(parameters->Class);
	}
	catch(Exception *ex)
	{
		throw ex;
	}
	__finally
	{
		assembly = NULL;
	}
}